<?php
if (!defined('ADODB_DIR')) die();
function adodb_pdo_type($t)
{
	switch($t) {
	case 2: return 'VARCHAR';
	case 3: return 'BLOB';
	default: return 'NUMERIC';
	}
}
class ADODB_pdo extends ADOConnection {
	var $databaseType = "pdo";	
	var $dataProvider = "pdo";
	var $fmtDate = "'Y-m-d'";
	var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
	var $replaceQuote = "''";
	var $hasAffectedRows = true;
	var $_bindInputArray = true;	
	var $_genSeqSQL = "create table %s (id integer)";
	var $_autocommit = true;
	var $_haserrorfunctions = true;
	var $_lastAffectedRows = 0;
	
	var $_errormsg = false;
	var $_errorno = false;
	
	var $dsnType = '';
	var $stmt = false;
	
	function ADODB_pdo()
	{
	}
	
	function _UpdatePDO()
	{
		$d = $this->_driver;
		$this->fmtDate = $d->fmtDate;
		$this->fmtTimeStamp = $d->fmtTimeStamp;
		$this->replaceQuote = $d->replaceQuote;
		$this->sysDate = $d->sysDate;
		$this->sysTimeStamp = $d->sysTimeStamp;
		$this->random = $d->random;
		$this->concat_operator = $d->concat_operator;
		$this->nameQuote = $d->nameQuote;
				
		$this->hasGenID = $d->hasGenID;
		$this->_genIDSQL = $d->_genIDSQL;
		$this->_genSeqSQL = $d->_genSeqSQL;
		$this->_dropSeqSQL = $d->_dropSeqSQL;

		$d->_init($this);
	}
	
	function Time()
	{
		if (!empty($this->_driver->_hasdual)) $sql = "select $this->sysTimeStamp from dual";
		else $sql = "select $this->sysTimeStamp";
		
		$rs = $this->_Execute($sql);
		if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
		
		return false;
	}
	
	// returns true or false
	function _connect($argDSN, $argUsername, $argPassword, $argDatabasename, $persist=false)
	{
		$at = strpos($argDSN,':');
		$this->dsnType = substr($argDSN,0,$at);

		if ($argDatabasename) {
			$argDSN .= ';dbname='.$argDatabasename;
		}
		try {
			$this->_connectionID = new PDO($argDSN, $argUsername, $argPassword);
		} catch (Exception $e) {
			$this->_connectionID = false;
			$this->_errorno = -1;
			//var_dump($e);
			$this->_errormsg = 'Connection attempt failed: '.$e->getMessage();
			return false;
		}
		
		if ($this->_connectionID) {
			switch(ADODB_ASSOC_CASE){
			case 0: $m = PDO::CASE_LOWER; break;
			case 1: $m = PDO::CASE_UPPER; break;
			default:
			case 2: $m = PDO::CASE_NATURAL; break;
			}
			$this->_connectionID->setAttribute(PDO::ATTR_CASE,$m);
			
			$class = 'ADODB_pdo_'.$this->dsnType;
			switch($this->dsnType) {
			case 'oci':
			case 'mysql':
			case 'pgsql':
			case 'mssql':
			case 'sqlite':
				include_once(ADODB_DIR.'/drivers/adodb-pdo_'.$this->dsnType.'.inc.php');
				break;
			}
			if (class_exists($class))
				$this->_driver = new $class();
			else
				$this->_driver = new ADODB_pdo_base();
			
			$this->_driver->_connectionID = $this->_connectionID;
			$this->_UpdatePDO();
			return true;
		}
		$this->_driver = new ADODB_pdo_base();
		return false;
	}
	
	function Concat() 
	{
		$args = func_get_args();
		if(method_exists($this->_driver, 'Concat')) 
			return call_user_func_array(array($this->_driver, 'Concat'), $args); 
		
		return call_user_func_array(array($this,'parent::Concat'), $args); 
	}
	
	// returns true or false
	function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
	{
		return $this->_connect($argDSN, $argUsername, $argPassword, $argDatabasename, true);
	}
	
	/*------------------------------------------------------------------------------*/
	
	
	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) 
	{	
		$save = $this->_driver->fetchMode;
		$this->_driver->fetchMode = $this->fetchMode;
	 	$this->_driver->debug = $this->debug;
		$ret = $this->_driver->SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
		$this->_driver->fetchMode = $save;
		return $ret;
	}
	
	
	function ServerInfo()
	{
		return $this->_driver->ServerInfo();
	}
	
	function MetaTables($ttype=false,$showSchema=false,$mask=false)
	{
		return $this->_driver->MetaTables($ttype,$showSchema,$mask);
	}
	
	function MetaColumns($table,$normalize=true)
	{
		return $this->_driver->MetaColumns($table,$normalize);
	}
	
	function InParameter(&$stmt,&$var,$name,$maxLen=4000,$type=false)
	{
		$obj = $stmt[1];
		if ($type) $obj->bindParam($name,$var,$type,$maxLen);
		else $obj->bindParam($name, $var);
	}
	
	function OffsetDate($dayFraction,$date=false)
    {   
        return $this->_driver->OffsetDate($dayFraction,$date);
    }
	
	function ErrorMsg()
	{
		if ($this->_errormsg !== false) return $this->_errormsg;
		if (!empty($this->_stmt)) $arr = $this->_stmt->errorInfo();
		else if (!empty($this->_connectionID)) $arr = $this->_connectionID->errorInfo();
		else return 'No Connection Established';
		
		
		if ($arr) {
		 	if (sizeof($arr)<2) return '';
			if ((integer)$arr[0]) return $arr[2];
			else return '';
		} else return '-1';
	}
	

	function ErrorNo()
	{
		if ($this->_errorno !== false) return $this->_errorno;
		if (!empty($this->_stmt)) $err = $this->_stmt->errorCode();
		else if (!empty($this->_connectionID)) {
			$arr = $this->_connectionID->errorInfo();
			if (isset($arr[0])) $err = $arr[0];
			else $err = -1;
		} else
			return 0;
			
		if ($err == '00000') return 0; // allows empty check
		return $err;
	}

	function SetTransactionMode($transaction_mode) 
	{
		if(method_exists($this->_driver, 'SetTransactionMode')) 
			return $this->_driver->SetTransactionMode($transaction_mode); 
		
		return parent::SetTransactionMode($seqname); 
	}

	function BeginTrans()
	{	
		if(method_exists($this->_driver, 'BeginTrans')) 
			return $this->_driver->BeginTrans(); 
		
		if (!$this->hasTransactions) return false;
		if ($this->transOff) return true; 
		$this->transCnt += 1;
		$this->_autocommit = false;
		$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,false);
		return $this->_connectionID->beginTransaction();
	}
	
	function CommitTrans($ok=true) 
	{ 
		if(method_exists($this->_driver, 'CommitTrans')) 
			return $this->_driver->CommitTrans($ok); 
		
		if (!$this->hasTransactions) return false;
		if ($this->transOff) return true; 
		if (!$ok) return $this->RollbackTrans();
		if ($this->transCnt) $this->transCnt -= 1;
		$this->_autocommit = true;
		
		$ret = $this->_connectionID->commit();
		$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true);
		return $ret;
	}
	
	function RollbackTrans()
	{
		if(method_exists($this->_driver, 'RollbackTrans')) 
			return $this->_driver->RollbackTrans(); 
		
		if (!$this->hasTransactions) return false;
		if ($this->transOff) return true; 
		if ($this->transCnt) $this->transCnt -= 1;
		$this->_autocommit = true;
		
		$ret = $this->_connectionID->rollback();
		$this->_connectionID->setAttribute(PDO::ATTR_AUTOCOMMIT,true);
		return $ret;
	}
	
	function Prepare($sql)
	{
		$this->_stmt = $this->_connectionID->prepare($sql);
		if ($this->_stmt) return array($sql,$this->_stmt);
		
		return false;
	}
	
	function PrepareStmt($sql)
	{
		$stmt = $this->_connectionID->prepare($sql);
		if (!$stmt) return false;
		$obj = new ADOPDOStatement($stmt,$this);
		return $obj;
	}
	
	function CreateSequence($seqname='adodbseq',$startID=1)
	{
		if(method_exists($this->_driver, 'CreateSequence')) 
			return $this->_driver->CreateSequence($seqname, $startID); 
		
		return parent::CreateSequence($seqname, $startID); 
	}
	
	function DropSequence($seqname='adodbseq')
	{
		if(method_exists($this->_driver, 'DropSequence')) 
			return $this->_driver->DropSequence($seqname); 
		
		return parent::DropSequence($seqname); 
	}

	function GenID($seqname='adodbseq',$startID=1)
	{
		if(method_exists($this->_driver, 'GenID')) 
			return $this->_driver->GenID($seqname, $startID); 
		
		return parent::GenID($seqname, $startID); 
	}

	
	/* returns queryID or false */
	function _query($sql,$inputarr=false) 
	{
		if (is_array($sql)) {
			$stmt = $sql[1];
		} else {
			$stmt = $this->_connectionID->prepare($sql);
		}
		if ($stmt) {
			$this->_driver->debug = $this->debug;
			if ($inputarr) $ok = $stmt->execute($inputarr);
			else $ok = $stmt->execute();
		} 
		
		
		$this->_errormsg = false;
		$this->_errorno = false;
			
		if ($ok) {
			$this->_stmt = $stmt;
			return $stmt;
		}
		
		if ($stmt) {
			
			$arr = $stmt->errorinfo();
			if ((integer)$arr[1]) {
				$this->_errormsg = $arr[2];
				$this->_errorno = $arr[1];
			}

		} else {
			$this->_errormsg = false;
			$this->_errorno = false;
		}
		return false;
	}

	// returns true or false
	function _close()
	{
		$this->_stmt = false;
		return true;
	}

	function _affectedrows()
	{
		return ($this->_stmt) ? $this->_stmt->rowCount() : 0;
	}
	
	function _insertid()
	{
		return ($this->_connectionID) ? $this->_connectionID->lastInsertId() : 0;
	}
}

class ADODB_pdo_base extends ADODB_pdo {

	var $sysDate = "'?'";
	var $sysTimeStamp = "'?'";
	

	function _init($parentDriver)
	{
		$parentDriver->_bindInputArray = true;
		#$parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true);
	}
	
	function ServerInfo()
	{
		return ADOConnection::ServerInfo();
	}
	
	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0)
	{
		$ret = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
		return $ret;
	}
	
	function MetaTables()
	{
		return false;
	}
	
	function MetaColumns()
	{
		return false;
	}
}

class ADOPDOStatement {

	var $databaseType = "pdo";		
	var $dataProvider = "pdo";
	var $_stmt;
	var $_connectionID;
	
	function ADOPDOStatement($stmt,$connection)
	{
		$this->_stmt = $stmt;
		$this->_connectionID = $connection;
	}
	
	function Execute($inputArr=false)
	{
		$savestmt = $this->_connectionID->_stmt;
		$rs = $this->_connectionID->Execute(array(false,$this->_stmt),$inputArr);
		$this->_connectionID->_stmt = $savestmt;
		return $rs;
	}
	
	function InParameter(&$var,$name,$maxLen=4000,$type=false)
	{

		if ($type) $this->_stmt->bindParam($name,$var,$type,$maxLen);
		else $this->_stmt->bindParam($name, $var);
	}
	
	function Affected_Rows()
	{
		return ($this->_stmt) ? $this->_stmt->rowCount() : 0;
	}
	
	function ErrorMsg()
	{
		if ($this->_stmt) $arr = $this->_stmt->errorInfo();
		else $arr = $this->_connectionID->errorInfo();

		if (is_array($arr)) {
			if ((integer) $arr[0] && isset($arr[2])) return $arr[2];
			else return '';
		} else return '-1';
	}
	
	function NumCols()
	{
		return ($this->_stmt) ? $this->_stmt->columnCount() : 0;
	}
	
	function ErrorNo()
	{
		if ($this->_stmt) return $this->_stmt->errorCode();
		else return $this->_connectionID->errorInfo();
	}
}
class ADORecordSet_pdo extends ADORecordSet {	
	
	var $bind = false;
	var $databaseType = "pdo";		
	var $dataProvider = "pdo";
	
	function ADORecordSet_pdo($id,$mode=false)
	{
		if ($mode === false) {  
			global $ADODB_FETCH_MODE;
			$mode = $ADODB_FETCH_MODE;
		}
		$this->adodbFetchMode = $mode;
		switch($mode) {
		case ADODB_FETCH_NUM: $mode = PDO::FETCH_NUM; break;
		case ADODB_FETCH_ASSOC:  $mode = PDO::FETCH_ASSOC; break;
		
		case ADODB_FETCH_BOTH: 
		default: $mode = PDO::FETCH_BOTH; break;
		}
		$this->fetchMode = $mode;
		
		$this->_queryID = $id;
		$this->ADORecordSet($id);
	}

	
	function Init()
	{
		if ($this->_inited) return;
		$this->_inited = true;
		if ($this->_queryID) @$this->_initrs();
		else {
			$this->_numOfRows = 0;
			$this->_numOfFields = 0;
		}
		if ($this->_numOfRows != 0 && $this->_currentRow == -1) {
			$this->_currentRow = 0;
			if ($this->EOF = ($this->_fetch() === false)) {
				$this->_numOfRows = 0; // _numOfRows could be -1
			}
		} else {
			$this->EOF = true;
		}
	}
	
	function _initrs()
	{
	global $ADODB_COUNTRECS;
	
		$this->_numOfRows = ($ADODB_COUNTRECS) ? @$this->_queryID->rowCount() : -1;
		if (!$this->_numOfRows) $this->_numOfRows = -1;
		$this->_numOfFields = $this->_queryID->columnCount();
	}

	// returns the field object
	function FetchField($fieldOffset = -1) 
	{
		$off=$fieldOffset+1; // offsets begin at 1
		
		$o= new ADOFieldObject();
		$arr = @$this->_queryID->getColumnMeta($fieldOffset);
		if (!$arr) {
			$o->name = 'bad getColumnMeta()';
			$o->max_length = -1;
			$o->type = 'VARCHAR';
			$o->precision = 0;
	#		$false = false;
			return $o;
		}
		//adodb_pr($arr);
		$o->name = $arr['name'];
		if (isset($arr['native_type']) && $arr['native_type'] <> "null") $o->type = $arr['native_type'];
		else $o->type = adodb_pdo_type($arr['pdo_type']);
		$o->max_length = $arr['len'];
		$o->precision = $arr['precision'];
		
		if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name);
		else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name);
		return $o;
	}
	
	function _seek($row)
	{
		return false;
	}
	
	function _fetch()
	{
		if (!$this->_queryID) return false;
		
		$this->fields = $this->_queryID->fetch($this->fetchMode);
		return !empty($this->fields);
	}
	
	function _close() 
	{
		$this->_queryID = false;
	}
	
	function Fields($colname)
	{
		if ($this->adodbFetchMode != ADODB_FETCH_NUM) return @$this->fields[$colname];
		
		if (!$this->bind) {
			$this->bind = array();
			for ($i=0; $i < $this->_numOfFields; $i++) {
				$o = $this->FetchField($i);
				$this->bind[strtoupper($o->name)] = $i;
			}
		}
		 return $this->fields[$this->bind[strtoupper($colname)]];
	}

}

?>